package com.lx.chong.service.impl;


import com.lx.chong.dto.PageAndSortDto;
import com.lx.chong.dto.QueryDto;
import com.lx.chong.enums.ResultEnum;
import com.lx.chong.service.BaseService;
import com.lx.chong.utils.pageable.MyQuery;
import com.lx.chong.utils.pageable.MyQueryType;
import com.lx.chong.utils.pageable.PageableUtil;
import com.lx.chong.utils.pageable.QueryDto2MyQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Service;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author: whh
 * @date: 2017/12/18
 */
@Service
@Slf4j
public class BaseServiceImpl implements BaseService {

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private QueryDto2MyQuery queryDto2MyQuery;

    @Override
    public Page getPageInfoFromParams(PageRequest pageable, List<MyQuery> queries, JpaSpecificationExecutor repository) {
        try {
            return repository.findAll((root, cq, cb) -> {
                List<Predicate> lstPredicates = new ArrayList<>();
                for (MyQuery myQuery : queries) {
                    String param = myQuery.getParam();
                    if (param == null) {
                        continue;
                    }
                    MyQueryType tempType = myQuery.getType();
                    Object[] value = myQuery.getValue();

                    String[] split = param.split("\\.", -1);
                    // Path
                    Path temp = root;
                    for (String s : split) {
                        temp = temp.get(s);
                    }
                    Predicate thisPr = null;
                    try {
                        thisPr = getPredicateFromPathAndTypeAndParams(thisPr, cb, temp, tempType, value);
                        lstPredicates.add(thisPr);
                    } catch (ParseException e) {
                        log.error(e.getMessage());
                    }
                }
                return cb.and(lstPredicates.toArray(new Predicate[]{}));
            }, pageable);
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

    @Override
    public List getListInfoFromParams(List<MyQuery> queries, JpaSpecificationExecutor repository) {
        try {
            return repository.findAll((root, cq, cb) -> {
                List<Predicate> lstPredicates = new ArrayList<>();
                for (MyQuery myQuery : queries) {
                    String param = myQuery.getParam();
                    if (param == null) {
                        continue;
                    }
                    MyQueryType tempType = myQuery.getType();
                    Object[] value = myQuery.getValue();

                    String[] split = param.split("\\.", -1);
                    // Path
                    Path temp = root;
                    for (String s : split) {
                        temp = temp.get(s);
                    }
                    Predicate thisPr = null;
                    try {
                        thisPr = getPredicateFromPathAndTypeAndParams(thisPr, cb, temp, tempType, value);
                        lstPredicates.add(thisPr);
                    } catch (ParseException e) {
                        log.error(e.getMessage());
                    }
                }
                return cb.and(lstPredicates.toArray(new Predicate[]{}));
            });
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

    /**
     * 拼接Criteria查询条件
     *
     * @param pr     Predicate上下文对象
     * @param cb     CriteriaBuilder
     * @param path   对象层级路径 eq: Class:Apple -> boy -> name root即为 Apple
     * @param type   自定义的枚举类型与 cb接口中封装的方法一致
     * @param params 参数数组
     * @return 拼接条件后的上下文对象
     * @throws ParseException
     */
    private Predicate getPredicateFromPathAndTypeAndParams(Predicate pr, CriteriaBuilder cb, Path path, MyQueryType type, Object[] params) throws ParseException {
        SimpleDateFormat formatDateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat formatDate = new SimpleDateFormat("yyyy-MM-dd");
        switch (type) {
            case equal:
                return cb.equal(path, params[0]);
            case like:
                return cb.like(path, "%" + params[0] + "%");
            case lLike:
                return cb.like(path, "%" + params[0]);
            case rLike:
                return cb.like(path, params[0] + "%");
            case or:
                return cb.or(pr);
            case gt:
                return cb.gt(path, Double.parseDouble(params[0] + ""));
            case lt:
                return cb.lt(path, Double.parseDouble(params[0] + ""));
            case ge:
                return cb.ge(path, Double.parseDouble(params[0] + ""));
            case le:
                return cb.le(path, Double.parseDouble(params[0] + ""));
            case greaterThan:
                return cb.greaterThan(path, new Date());
            case lessThan:
                return cb.lessThan(path, new Date());
            case greaterThanEqual:
                return cb.greaterThanOrEqualTo(path, new Date());
            case lessThanEqual:
                return cb.lessThanOrEqualTo(path, new Date());
            case in:
                return path.in(params);
            case betweenNum:
                return cb.between(path, Double.parseDouble(params[0] + ""), Double.parseDouble(params[1] + ""));
            case betweenDate:
                // yyyy-MM-dd 需要将后一个日期延续一天 2018-01-01 - 2018-01-01 ===> 2018-01-01 00:00:00 - 2018-01-02 23:59:59
                return cb.between(path, formatDateTime.parse(params[0] + " 00:00:00"), formatDateTime.parse(params[1] + " 23:59:59"));
            case betweenDateTime:
                return cb.between(path, formatDateTime.parse(params[0] + ""), formatDateTime.parse(params[1] + ""));
            case and:
                return cb.and(pr);
            case isNull:
                return cb.isNull(path);
            case isNotNull:
                return cb.isNotNull(path);
            case notLike:
                return cb.notLike(path, "%" + params[0] + "%");
            case notEqual:
                return cb.notEqual(path, params[0]);
            default:
                throw new RuntimeException();
        }
    }

    /**
     * 修改,新增属性判断是否重复
     *
     * @param entityName 实体类名
     * @param para       需要判断的集合对象(key:属性名,value:实际的参数值)
     * @param id         修改判断时，当前实体类的id
     * @param flag       可变参，针对字段中不存在ifDelete情况
     * @return true:不重复,false:重复或异常
     */
    @Override
    public boolean checkRepeat(String entityName, Map<String, Object> para, Object id, boolean... flag) {
        try {
            String hql = "from " + entityName + " c where 1=1 ";
            for (Map.Entry<String, Object> entry : para.entrySet()) {
                String key = String.valueOf(entry.getKey());
                Object value = StringUtils.trim(entry.getValue().toString());
                if (entityName.equals("Person") && key.equals("mobile")) {
                    hql += " and (c." + key + "='" + StringEscapeUtils.escapeSql(value.toString()) +
                            "' or c.account='" + StringEscapeUtils.escapeSql(value.toString()) + "')";
                } else if (entityName.equals("Person") && key.equals("account")) {
                    hql += " and (c." + key + "='" + StringEscapeUtils.escapeSql(value.toString()) +
                            "' or c.mobile='" + StringEscapeUtils.escapeSql(value.toString()) + "')";
                } else {
                    hql += " and c." + key + "='" + StringEscapeUtils.escapeSql(value.toString()) + "'";
                }
            }
            if(entityName.equals("Person") && para.get("account")!=null && para.get("account").toString().equalsIgnoreCase("admin")){
                return false;
            }
            // 修改时,需要把当前id所属的对象除去
            if (id != null) {
                hql += " and c.id <> " + Long.parseLong(id.toString());
            }
            if (flag.length == 0) {
                hql += " and c.ifDelete = 0 ";
            }
            String role = para.get("role") == null ? "" : para.get("role").toString();
            if (!role.equals("0") && entityName.equals("Person")) {
                hql += " and c.role <> 0 ";
            }
            int i = entityManager.createQuery(hql).getResultList().size();
            if (i == 0) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
           return false;
        }
    }

    /**
     * 修改,新增属性判断是否重复,适用与父子关系，不同父级下同级名称可重复
     *
     * @param entityName 实体类名
     * @param para       需要判断的集合对象(key:属性名,value:实际的参数值)
     * @param id         修改判断时，当前实体类的id
     * @param flag       可变参，针对字段中不存在ifDelete情况
     * @return true:不重复,false:重复或异常
     */
    @Override
    public boolean checkRepeat(String entityName, Map<String, Object> para, Object id, Long parentId, String fieldName, boolean... flag) {
        try {
            String hql = "from " + entityName + " c where 1=1 ";
            for (Map.Entry<String, Object> entry : para.entrySet()) {
                String key = String.valueOf(entry.getKey());
                Object value = StringUtils.trim(entry.getValue().toString());
                hql += " and c." + key + "='" + StringEscapeUtils.escapeSql(value.toString()) + "'";
            }
            // 修改时,需要把当前id所属的对象除去
            if (id != null) {
                hql += " and c.id <> " + Long.parseLong(id.toString());
            }
            if (parentId != null) {
                hql += " and c." + fieldName + " = " + parentId.toString();
            }
            if (flag.length == 0) {
                hql += " and c.ifDelete = 0 ";
            }
            int i = entityManager.createQuery(hql).getResultList().size();
            if (i == 0) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public <T> Page<T> getPageAndSortThis(PageAndSortDto dto, JpaSpecificationExecutor repository) {
        PageRequest pageable = null;
        if (dto.getPage() != null){
            pageable = PageableUtil.buildPageRequest(dto.getPage(), dto.getSorts());
        }
        List<QueryDto> queryList = dto.getQueryList();
        List<MyQuery> parse = queryDto2MyQuery.parse(queryList);
        return getPageInfoFromParams(pageable, parse, repository);
    }

    public <T> List<T> getListInfoAndSortThis(PageAndSortDto dto, JpaSpecificationExecutor repository) {
        List<QueryDto> queryList = dto.getQueryList();
        List<MyQuery> parse = queryDto2MyQuery.parse(queryList);
        return getListInfoFromParams(parse, repository);
    }

}
